package ddalarm

import (
	"bytes"
	"context"
	"encoding/json"
	"sync"
	"time"

	"github.com/pkg/errors"

	"testkjcld.bhecard.com/gitlab/kit/esim/config"
	"testkjcld.bhecard.com/gitlab/kit/esim/http"
	"testkjcld.bhecard.com/gitlab/kit/esim/log"
)

var (
	alarm *Client
	once  sync.Once
)

type Client struct {
	config *Config
	http   *http.Client
	conf   config.Config
	logger log.Logger
}

type Options func(*Client)

func NewAlarm(opts ...Options) *Client {
	once.Do(func() {
		alarm = &Client{config: new(Config)}
		for _, opt := range opts {
			opt(alarm)
		}

		if alarm.logger == nil {
			alarm.logger = log.NewLogger()
		}

		if alarm.conf == nil {
			alarm.conf = config.NewMemConfig()
		}

		alarm.config.setDefaultConfig(alarm.conf)

		// http transport 连接
		var opts http.ClientOptions
		if alarm.http == nil {
			alarm.http = http.NewClient(
				opts.WithInsecureSkip(),
				opts.WithDisableKeepAlive(true),
				opts.WithTimeOut(30*time.Second),
				opts.WithHttpProxy(alarm.config.ProxyURL),
			)
		}
	})

	return alarm
}

func WithHttp(http *http.Client) Options {
	return func(c *Client) {
		c.http = http
	}
}

func WithConf(conf config.Config) Options {
	return func(c *Client) {
		c.conf = conf
	}
}

func WithLogger(logger log.Logger) Options {
	return func(c *Client) {
		c.logger = logger
	}
}

type AtContent struct {
	AtMobiles []string `json:"atMobiles"` // 被@人的手机号（在content里添加@人的手机号
	IsAtAll   bool     `json:"isAtAll"`   // 是否@所有人
}

type Content struct {
	Content string `json:"content"` // 文本消息
}

type Request struct {
	MsgType string     `json:"msgtype"` // 消息类型
	At      *AtContent `json:"at"`      // @人数
	Text    *Content   `json:"text"`
}

func (c *Client) NewRequest(message string) *Request {
	return &Request{
		MsgType: c.config.MsgType,
		At:      c.config.At,
		Text: &Content{
			message,
		},
	}
}

func (r *Request) Marshal() []byte {
	d, _ := json.Marshal(r)
	return d
}

func (r *Request) SetMobileList(list []string) {
	if list == nil {
		return
	}
	if r.At == nil {
		r.At = &AtContent{
			AtMobiles: nil,
			IsAtAll:   false,
		}
	}
	if list[0] == "@all" {
		r.At.IsAtAll = true
		return
	}

	if list[0] == "" {
		return
	}
	r.At.AtMobiles = list
	r.At.IsAtAll = false
}

type Response struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

func (c *Client) Send(ctx context.Context, message string) error {
	request := c.NewRequest(message)

	// 调用钉钉接口
	resp, err := c.http.Client.R().SetHeader("Content-Type", "application/json;charset=utf-8").
		SetBody(bytes.NewBuffer(request.Marshal())).
		Post(c.config.builderURL())
	if err != nil {
		c.logger.Errorc(ctx, "发送报警失败: %v", err)
		return err
	}

	ret := new(Response)
	err = json.Unmarshal(resp.Body(), ret)
	if err != nil {
		c.logger.Errorc(ctx, "报警响应解析失败%v", err)
	}

	if ret.ErrCode != 0 {
		c.logger.Errorc(ctx, "报警响应Code不正确%v", ret)
		return errors.New("alarm_resp_code_error")
	}

	return err
}

func (c *Client) SendWithMobileList(ctx context.Context, message string, mobileList []string) error {
	request := c.NewRequest(message)
	request.SetMobileList(mobileList)

	// 调用钉钉接口
	resp, err := c.http.Client.R().SetHeader("Content-Type", "application/json;charset=utf-8").
		SetBody(bytes.NewBuffer(request.Marshal())).
		Post(c.config.builderURL())
	if err != nil {
		c.logger.Errorc(ctx, "发送报警失败: %v", err)
		return err
	}

	ret := new(Response)
	err = json.Unmarshal(resp.Body(), ret)
	if err != nil {
		c.logger.Errorc(ctx, "报警响应解析失败%v", err)
	}

	if ret.ErrCode != 0 {
		c.logger.Errorc(ctx, "报警响应Code不正确[%v]", ret)
		return errors.New("alarm_resp_code_error")
	}

	return err
}
